;
;  PROGRAM:  ZEX
;  AUTHOR:  RICHARD CONN (DERIVED FROM EX, WHICH WAS WRITTEN BY SOMEONE ELSE)
;  VERSION:  3.1
;  DATE:  24 Oct 84
;  PREVIOUS VERSIONS:  3.0 (8 Mar 84)
;
VERS	EQU	31
Z3ENV	EQU	0F400H

;
;   ZEX 3.1 -- Richard Conn
;	Does not require to be reassembled for a target system; Z3INS
;	installation is sufficient
;   ZEX 3.0 -- Derived from ZEX 1.3 for ZCPR3 by Richard Conn
;	Extensions to ZEX 1.3 are:
;		ZCPR3 Message Passing Employed
;		ZCPR3 Structure
;		Omitted Parameters Allowed
;		Print Suppression During FALSE IFs (^&)
;
;   ZEX 1.0 -- EX 1.2.1 implemented for ZCPR2 by Richard Conn
;		DATE:  12 NOV 82
;	Extensions to EX are:
;		Multiple command buffer is preserved, and any commands
;			following the ZEX command are executed after
;			the ZEX command file is completed (ZEX T;DIR
;			will execute commands in T.SUB and then run DIR)
;		ZCPR3 Search Path is following when looking for the
;			file specified to ZEX
;		Command File Type may be SUB or ZEX
;		Added ^* form to simply ring the bell
;		Added ^/ form to act like ^? but ring bell periodically
;		Added ^" form to allow user input in the middle of a
;			command file operation
;		Major rewrite of EX to improve readability and to impose
;			a structured organization on the code for maintenance
;			purposes
;		Major change in the abort system so that the multiple command
;			line buffer of ZCPR3 will be properly cleared on
;			abort; without this change, ZEX would crash the system
;			in attempting to abort out of the ^/ and ^? forms;
;			EX will probably always crash a ZCPR3 system with
;			multiple commands enabled if an abort from ^? is
;			attempted
;
;   EX12.ASM - An enhanced version of EXEC and EX.
;
;   START   05-09-82
;
;   DATE    08-11-82  *LAST MAJOR CHANGE
;
;   HISTORY:
;
;   ZEX 1.0 11-12-82  modify for use under ZCPR2; main change is to place
;			rest of multiple command line at end of SUB file
;
;   1.2.1   09-16-82  fix for MBASIC execution under EX 1.2 .
;
;   1.2     08-11-82  added '^:' EX runtime re-execute logic function,
;			    '^?' EX runtime wait for carriage return,
;			    logic to prevent input/EX buffer overlap,
;			    logic to insure (Xsub Already Present),
;			    logic to prevent EX runtime recursion loop,
;			and prompt character logic	   [Larry Steeger]
;
;   1.1     08-06-82  added ';;' EX comment's support,
;			    '^.' print suppression function,
;			    '^<...^>' immediate display support,
;			    '^#' EX message suppression function,
;			    '^$' default parameter support,
;			and '^|' cr,lf generation function [Larry Steeger]
;
;   1.0     08-03-82  corrected $^ error and ^<lowercase> error [Larry Steeger]
;
;   ?	    06-19-82  added missing TRUE and FALSE equates [Ron Fowler]
;
;   ?	    05-17-82  corrected last cold boot no active message
;

;
;	EX12.COM IS AN ENHANCEMENT OF EXEC.COM AND EX.COM
;
;	OPTIONS:
;
;	EX <subfile> <parameters> cr
;
;	EX cr
;
;	 ^<?> WILL GIVE CONTROL CHARACTER <?>
;
;	 | WILL BE CR
;
;	 ^| WILL BE CR,LF
;
;	 ^: WILL CAUSE ZEX TO RE-EXECUTE THE .SUB FILE FROM THE BEGINNING
;
;	 ^? WILL CAUSE ZEX TO WAIT FOR A CARRIAGE RETURN
;	    (^C WILL ABORT ZEX AT THIS POINT ALSO)
;
;	 ^/ WILL CAUSE ZEX TO RING THE BELL AND WAIT FOR A CARRIAGE RETURN
;	    (^C WILL ABORT ZEX AT THIS POINT ALSO)
;
;	 ^* WILL CAUSE ZEX TO RING THE BELL
;
;	 ^" WILL CAUSE ZEX TO STOP PROVIDING INPUT UNTIL THE ZCPR3 MESSAGE
;	    CONTROLLING ZEX IS CHANGED TO PERMIT CONTINUATION
;
;	 ^$ WILL CAUSE THE REST OF THE LINE TO BE TREATED AS A
;	    SET OF DEFAULT PARAMETERS SEPARATED BY BLANKS TO BE
;	    USED IF THE USER HAS NOT PROVIDED ONE ON ZEX'S COMMAND LINE.
;
;	 ^# WILL TOGGLE PRINT SUPPRESSION OF ZEX MESSAGES
;
;	 ^. WILL START PRINT SUPPRESSION OF ALL CHARACTERS
;	    FROM .SUB FILE UNTIL A SUBSEQUENT ^. IS ENCOUNTERED
;
;	 ^& WILL INSTRUCT ZEX TO SUPPRESS PRINT IF WITHIN A FALSE IF
;
;	 ;; WILL INDICATE THAT THE ;; AND ALL CHARACTERS FOLLOWING IT
;	    UNTIL A LF IS ENCOUNTERED ARE NOT INCLUDED IN ZEX'S
;	    TEXT BUFFER
;	    (I.E. A ZEX ONLY COMMENT)
;
;	 ^<  WILL START IMMEDIATE DISPLAY OF CHARACTERS FROM
;	    THE .SUB FILE UNTIL ^> IS ENCOUNTERED
;	    (I.E. DISPLAY ONLY .SUB INPUT)
;
;	 $<1-9> WILL REPLACE PARAMETER<1-9> IN TEXT FROM THE COMMAND LINE
;
;	 $$ WILL GIVE $
;
;	 $^ WILL GIVE ^
;
;	 $| WILL GIVE |
;
;	 |,cr,lf,1ah will eat last from | to end of buffer
;
;	 ^C FROM CONSOLE WILL ABORT ZEX
;

FALSE	EQU	0
TRUE	EQU	NOT FALSE

;
;  OFFSETS TO ZCPR3 ENVIRONMENT DESCRIPTOR ELEMENTS
;
EPOFF	EQU	9	;EXTERNAL PATH DATA
MCOFF	EQU	24	;COMMAND LINE DATA
Z3MOFF	EQU	34	;MESSAGE BUFFER

;
;  GENERAL EQUATES
;
BELL	EQU	7
CTRLZ	EQU	1AH	;^Z
DELAY	EQU	6000H	;DELAY CONSTANT FOR TIMER LOOP
BS	EQU	'H'-'@'	;BACKSPACE
CR	EQU	0DH
LF	EQU	0AH

;
;  ZEX MONITOR COMMAND BYTES
;
PSUP	EQU	80H	;^. PRINT SUPPRESS FLAG
IMON	EQU	81H	;^< IMMEDIATE MODE START
IMOFF	EQU	82H	;^> IMMEDIATE MODE STOP
MSUP	EQU	83H	;^# ZEX MESSAGE SUPPRESS FLAG
CRWAIT	EQU	84H	;^? ZEX RUNTIME WAIT FOR CR FLAG
REXEC	EQU	85H	;^: ZEX RUNTIME RE-EXECUTE FLAG
CRBWAIT	EQU	86H	;^/ ZEX RUNTIME RING BELL AND WAIT FOR CR FLAG
RNG	EQU	87H	;^* ZEX RUNTIME RING BELL
UICH	EQU	88H	;^" USER INPUT COMMAND CHAR SEQUENCE
IPS	EQU	89H	;^& FALSE IF PRINT SUPPRESS
;
;  CP/M CONSTANTS
;
WARM	EQU	0
BDISK	EQU	4
BDOS	EQU	5
DFCB	EQU	5CH
BUFF	EQU	80H
;
;	NOTE: ZEX30.LIB IS CREATED BY THE ZEX30.ZEX GENERATION PROCESS
;
	MACLIB	ZEX30
;
$-PRINT
	IF	ZEXBASE
$+PRINT
;
;	START OF ZEX INITIATOR CODE SEGMENT
;
	ORG	100H
;
; Environment Definition
;
	if	z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
	jmp	start
	db	'Z3ENV'	;This is a ZCPR3 Utility
	db	1	;External Environment Descriptor
z3eadr:
	dw	z3env
start:
	lhld	z3eadr	;pt to ZCPR3 environment
;
	else
;
; Internal ZCPR3 Environment Descriptor
;
	MACLIB	SYSENV.LIB
z3eadr:
	jmp	start
	SYSENV
start:
	lxi	h,z3eadr	;pt to ZCPR3 environment
	endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
	call	z3init	;initialize the ZCPR3 Env
	JMP	START0
;
;  INITIAL COMMAND LINE AREA
;
	DB	0FFH	;SIZE OF COMMAND LINE
DBUFF	EQU	$
	DB	0FFH	;SIZE OF BUFFER
	DS	100H	;SPACE FOR COMMAND LINE
;
;  START OF ZEX
;
START0:
	NOP		;REPLACED WITH RET TO PREVENT REENTRY
	LDA	DFCB+1	;CHECK FOR HELP REQUEST
	CPI	'/'	;HELP?
	JZ	HELP
	LXI	H,0
	DAD	SP
	SHLD	CCPSTK	;CCP STACK PTR
	LXI	SP,CCPSTK	;USER STACK AREA
	MVI	A,0C9H	; (8080 RET)
	STA	START	;PREVENT RE-ENTRANCE BY ZCPR
	LXI	H,BUFF	;COPY INPUT LINE INTO DBUFF
	LXI	D,DBUFF
	MVI	B,128	;SIZE OF BUFFER
	CALL	MOVE
	LXI	D,SIGNON	;LOGO
	CALL	PRINT
	CALL	ZEXACTV	;CHECK FOR RECURSION
	CALL	ZRELOC	;RELOCATE ZEX MODULE
	CALL	ZPARMS	;EXTRACT PARAMETERS FROM COMMAND LINE
	LDA	DFCB+1	;CHECK TO SEE IF SUB FILE PRESENT
	CPI	' '	;<SP>=NO
	PUSH	PSW	;SAVE FLAG
	CNZ	OPENSB	;OPEN AND LOAD ZEX FILE IF PRESENT OR ABORT
	POP	PSW	;GET FLAG
	CZ	INPUTSB	;INPUT COMMANDS FROM USER
;
;  HL NOW POINTS TO BYTE AFTER LOADED TEXT
;
	CALL	ZMCL	;STORE REST OF MULTIPLE COMMAND LINE
	CALL	ZLINES	;COPY AND PROCESS COMMAND LINES
;
;  SET UP FOR ZEX EXECUTION AND RUN; HL PTS TO BOTTOM OF DATA AREA
;
	PUSH	H	;SAVE PTR TO END OF DATA
	LHLD	RELSTRT	;GET PTR TO START OF ZEX
	SHLD	GOADR	;SET ADDRESS TO RUN TO
	DCX	H	;PT TO START OF DATA AREA
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;STORE HL THERE (NEXT CHAR FOR ZEX)
	MVI	B,0BH	;MESSAGE OFFSET
	CALL	MSHLD	;STORE HL THERE (FIRST CHAR FOR ZEX)
	MVI	A,0FFH	;SET ZEX RUNNING FLAG
	MVI	B,08H	;MESSAGE OFFSET
	CALL	MSTA	;STORE A THERE (ZEX IS RUNNING)
	POP	H	;HL IS PTR TO END OF DATA
	MOV	M,A	;SET UP END OF DATA
	DCX	H
	LDA	BDOS+2	;SET UP BDOS JUMP TO PROTECT DATA
	MOV	M,A
	DCX	H
	LDA	BDOS+1
	MOV	M,A
	DCX	H
	MVI	M,JMP
	SHLD	BDOS+1	;SET NEW BDOS ADDRESS
	LXI	H,0	;ASSUME NO MULTIPLE COMMANDS
	LDA	MCAVAIL	;GET FLAG
	ORA	A	;0=NONE
	JZ	GOTOZEX	;NO MULTIPLE COMMANDS, SO BC=0
	LHLD	MCADR	;GET ADDRESS OF MULTIPLE COMMAND BUFFER
;
;  ZEX MONITOR ENTRY PARAMETERS --
;	HL	ADDRESS OF MULTIPLE COMMAND BUFFER OR 0 IF NONE
;
GOTOZEX:
	XCHG		;SAVE HL
	LHLD	Z3MSGA	;GET ADDRESS OF MESSAGES ...
	XCHG		; ... IN DE

GOADR	EQU	$+1
	JMP	$

;
; INIT ZCPR3 ENVIRONMENT
;
Z3INIT:
	PUSH	H	;SAVE PTR TO ENVIRONMENT
	PUSH	H
	LXI	D,Z3MOFF	;OFFSET TO MESSAGE BUFFER
	DAD	D
	MOV	A,M	;GET LOW
	INX	H
	MOV	H,M	;GET HIGH
	MOV	L,A	;HL IS ADDRESS OF MESSAGES
	SHLD	Z3MSGA	;SAVE ADDRESS
	POP	H
	LXI	D,MCOFF	;OFFSET TO CL DATA
	DAD	D
	MOV	E,M	;GET CL ADDRESS
	INX	H
	MOV	D,M
	INX	H	;GET CL SIZE
	MOV	A,M
	ORA	D	;IF ALL THREE VALUES ARE 0, THEN NO COMMAND LINE
	ORA	E
	STA	MCAVAIL	;SET AVAILABLE FLAG
	XCHG		;HL PTS TO CL
	SHLD	MCADR
	POP	H	;GET PTR TO ENVIRONMENT
	LXI	D,EPOFF	;OFFSET TO EXTERNAL PATH DATA
	DAD	D
	MOV	E,M	;GET EXTERNAL PATH ADDRESS
	INX	H
	MOV	D,M
	MOV	A,D	;CHECK FOR ANY
	ORA	E
	STA	EPAVAIL	;SET AVAILABLE FLAG
	XCHG
	SHLD	EPADR	;SET ADDRESS
	RET
;
; Z3INIT BUFFERS
;
INTPATH:		;INTERNAL PATH
	DB	1,0	;DISK A, USER 0
	DB	0,0	;END OF PATH
Z3MSGA:			;ADDRESS OF MESSAGES
	DW	0
MCAVAIL:		;MULTIPLE COMMAND LINE DATA
	DB	0
MCADR:
	DW	0
EPAVAIL:		;EXTERNAL PATH DATA
	DB	0
EPADR:
	DW	0

;
;  PRINT HELP MESSAGE FOR ZEX
;
HELP:
	LXI	D,SIGNON	;PRINT BANNER
	CALL	PRINT
	LXI	H,HMSG	;PRINT MESSAGE
	CALL	HPRINT
	MVI	C,1	;GET CHAR
	CALL	BDOS
	CPI	'C'-'@'	;^C?
	RZ
	LXI	D,CRLFS
	CALL	PRINT
	LXI	D,SIGNON
	CALL	PRINT
	LXI	H,HMSG1
HPRINT:
	MOV	A,M	;GET CHAR
	ORA	A	;DONE?
	RZ
	INX	H	;PT TO NEXT
	PUSH	H	;SAVE PTR
	MOV	E,A	;CHAR IN E
	MVI	C,2	;CONSOLE OUTPUT
	CALL	BDOS
	POP	H	;GET PTR
	JMP	HPRINT

HMSG:
	DB	CR,LF,'ZEX Syntax:'
	DB	CR,LF,'		ZEX <zexfile> <parameters>'
	DB	CR,LF,'or'
	DB	CR,LF,'		ZEX'
	DB	CR,LF
	DB	CR,LF,'The first form executes the indicated command file'
	DB	CR,LF,'(<subfile> may be of type ZEX or SUB, and if a ZEX and'
	DB	CR,LF,'SUB both exist, the ZEX file is used), passing to it'
	DB	CR,LF,'the parameters, similar to the way SUBMIT is used.'
	DB	CR,LF
	DB	CR,LF,'The second form allows the user to enter commands.'
	DB	CR,LF,'ZEX presents the user with a prompt like "n:", where'
	DB	CR,LF,'n is a line number, and the user may type in a command'
	DB	CR,LF,'line.  Input is terminated by simply striking the'
	DB	CR,LF,'RETURN key (empty input line).'
	DB	CR,LF
	DB	CR,LF,'ZEX can be aborted by ^C from console.'
	DB	CR,LF
	DB	CR,LF,'    Strike Any Key to Continue, ^C to Abort - ',0

HMSG1:
	DB	CR,LF
	DB	CR,LF,'ZEX supports an enhanced command processing facility'
	DB	CR,LF,'which recognizes the following commands.  These may be'
	DB	CR,LF,'embedded in the text of the command file or user'
	DB	CR,LF,'input and will be executed after processing begins.'
	DB	CR,LF
	DB	CR,LF,'	Cmd Meaning		Cmd Meaning'
	DB	CR,LF,'	 |  insert <CR>		^|  insert <CR> <LF>'
	DB	CR,LF,'	^:  rerun command file	^.  suppress print of chars'
	DB	CR,LF,'	^#  toggle ZEX msgs	^$  define default params'
	DB	CR,LF,'	^?  wait for user <CR>	^/  ring and wait for <CR>'
	DB	CR,LF,'	^*  ring bell		^"  allow user input'
	DB	CR,LF,'	^<  display chars only	^>  stop display'
	DB	CR,LF,'	;;  ZEX comment		$n  1<=n<=9 for param'
	DB	CR,LF,'	$$  =$			$^  =^'
	DB	CR,LF,'	$|  =|			^c  insert ctrl char c'
	DB	CR,LF,CR,LF,0


;
;  RELOCATE ZEX MODULE INTO HIGH MEMORY JUST BELOW ZCPR3
;
ZRELOC:
	LHLD	RELOCL	;GET RELOC PROGRAM LENGTH
	MOV	B,H	;BC=HL=RELOC PROGRAM LENGTH
	MOV	C,L
	PUSH	B	;SAVE LENGTH FOR FUTURE USE
	LHLD	BDOS+1	;GET BASE
	LXI	D,-806H ;GET BEFORE CCP
	DAD	D
	MOV	A,L	;SUBTRACT RELOC LENGTH
	SUB	C
	MOV	E,A
	MOV	A,H
	SBB	B
	MOV	D,A
	PUSH	D	;SAVE NEW TOP/START TO MOVE TO
	LXI	H,BEGREL	;START OF MOVE
OMOVE:
	MOV	A,B
	ORA	C
	JZ	MOVEND
	DCX	B
	MOV	A,M
	STAX	D
	INX	D
	INX	H
	JMP	OMOVE
;
MOVEND:
	POP	D	;GET START OF MOVED PROGRAM
	POP	B	;LENGTH OF MOVE PROGRAM
	PUSH	D	;SAVE PTR TO START OF PROGRAM
	PUSH	H	;START OF BIT MAP
	MOV	H,D	;MSB OFFSET
	MOV	L,E	;LSB OFFSET
OFFLUP:
	MOV	A,B	;TEST LENGTH
	ORA	C	;IF 0
	JZ	GOTO	;JUMP TO RELOCATED PROGRAM
	DCX	B	;DECREMENT COUNT
	LDA	COUNT
	INR	A
	STA	COUNT
	ANI	07H
	JNZ	OFFBIT	;NO
	XTHL		;YES, GET BIT MAP
	MOV	A,M	;GET NEXT BYTE
	INX	H	;INCREMENT BIT MAP POINTER
	XTHL		;SAVE FOR LATER
	STA	BITMAP	;KEEP BIT OFFSET
OFFBIT:
	LDA	BITMAP
	RAL		;TEST FOR OFFSET
	STA	BITMAP	;SAVE NEW BYTE
	JNC	NOFSET	;NO
	DCX	D	;GET BACK TO LSB
	LDAX	D
	ADD	L
	STAX	D
	INX	D	;MSB
	LDAX	D	;YES
	ADC	H	;ADD IN OFFSET
	STAX	D	;PUT IN MOVED PLACE
NOFSET:
	INX	D	;INCREMENT MOVED POINTER
	JMP	OFFLUP	;CONTINUE WITH RELOCATE
;
GOTO:
	POP	D	;RESTORE STACK
	POP	H	;PT TO FIRST BYTE OF PROGRAM
	SHLD	RELSTRT	;SAVE PTR
	DCX	H	;RELOCATE PROGRAM-1
	SHLD	OUTBUF	;SAVE PTR TO BYTE IN FRONT OF RELOCATED PROGRAM
	RET
;
;  GET PARAMETERS FROM COMMAND LINE
;	TERMINATE EACH PARAMETER WITH A BINARY ZERO, AND SET POINTERS
;	TO EACH PARAMETER
;
ZPARMS:
	LXI	D,DBUFF	;TERMINATE COMMAND LINE WITH CR
	LDAX	D	;GET CHAR COUNT
	INX	D	;PT TO FIRST CHAR
	PUSH	D
	MOV	L,A	;HL = NUMBER OF CHARS IN LINE
	MVI	H,0
	DAD	D	;PT TO AFTER LAST CHAR
	MVI	M,CR	;STORE <CR>
	LXI	H,PRMDMY	;START AT DUMMY PARAMETER FOR .SUB FILE SPEC
	PUSH	H
	LXI	B,PRMPNL+2
	XRA	A
	CALL	FILL	;CLEAR PTR AREA
	POP	H	;GET PTR TO POINTER FOR PARAMETER 0
	POP	D	;GET PTR TO FIRST CHAR IN LINE
	MVI	A,(PRMPNL/2)+1	;NUMBER OF PARAMETERS POSSIBLE, MAX
	STA	PRMMAX	;HIGHEST PARAMETER # + 1 for .SUB SPEC
;
;  PARAMETER EXTRACTION ROUTINE; HL PTS TO FIRST PARAM PTR, DE PTS TO LINE
;
PARMS:
	MVI	B,0	;CLEAR PARAMETER COUNTER
	XCHG
	SHLD	ERRLNE	;SAVE IN CASE OF ERROR
	XCHG
;
PARMSL:
	LDAX	D	;IGNORE LEADING SPACES
	INX	D
	CPI	CR
	JZ	ENDLNE
	CPI	' '
	JZ	PARMSL
	DCX	D	;BACK UP TO 1ST CHAR
	MOV	M,E	;SAVE ADDRESS IN TABLE
	INX	H
	MOV	M,D
	INX	H
	INR	B	;COUNT+1
	LDA	PRMMAX
	CMP	B
	JC	PRMTOO	;TOO MANY ARGUMENTS
;
ENDPRM:
	LDAX	D	;GO TO END OF PARAMETER
	INX	D
	CPI	CR
	JZ	ENDLNE
	CPI	' '	;SKIP UNTIL <SP>
	JNZ	ENDPRM
	XRA	A	;A=0 TO TERMINATE PARAM
	DCX	D	;PT TO <SP> FOLLOWING PARAM
	STAX	D	;TERMINATE PARAMETER
	INX	D	;PT TO CHAR AFTER <SP>
	JMP	PARMSL	;IGNORE SPACES BETWEEN PARAMETERS
ENDLNE:
	XRA	A	;STORE ZERO AFTER LAST PARAMETER
	DCX	D	;PT TO CR
	STAX	D	;TERMINATE LAST PARAMETER
	INX	D	;PT TO AFTER LAST PARAM
	MVI	A,CR	;STORE ENDING CR
	STAX	D
	RET
;
;  INPUT COMMAND LINES FROM USER
;
INPUTSB:
	LXI	H,0
	SHLD	LINES	;START LINE COUNTER
	MVI	A,0FFH	;SET BUFFER LENGTH
	STA	DBUFF-1
	LXI	H,BEGREL	;SET UP OUTPUT BUFFER
	SHLD	INBUF
GETLIN:
	CALL	CRLF
	LHLD	LINES
	INX	H
	SHLD	LINES
	CALL	DECOUT	;PRINT LINE #
	MVI	E,':'	;GET PROMPT
	CALL	OUTCHR
	MVI	E,' '
	CALL	OUTCHR
	LXI	D,DBUFF-1
	MVI	C,10	;READ CONSOLE BUFFER
	CALL	BDOS
	LXI	D,DBUFF
	LDAX	D	;GET LENGTH
	MOV	B,A
	INX	D
	LHLD	INBUF	;GET INPUT POINTER
	ORA	A	;SEE IF END
	RZ		;DONE WITH INPUT
	XCHG
	CALL	MOVE	;MOVE TO INPUT BUFFER
	XCHG
	MVI	M,CR
	INX	H
	MVI	M,LF
	INX	H
	SHLD	INBUF
	JMP	GETLIN
;
;  OPEN AND LOAD SUB FILE
;
OPENSB:
	CALL	PUTUD	;SAVE USER/DISK
;
;  SET UP TO READ ZEX FILE
;
	LXI	D,DFCB+9
	LXI	H,ZEXNAM	;MOVE 'SUB' TO DFCB FILE TYPE
	MVI	B,3
	CALL	MOVE
	XRA	A	;ZERO CR FIELD
	STA	DFCB+32
	LXI	D,BUFF	;SET DMA ADDRESS
	MVI	C,26	;SET DMA
	CALL	BDOS
	LXI	D,DFCB
	LXI	H,INTPATH	;PT TO INTERNAL PATH
	LDA	EPAVAIL	;EXTERNAL PATHS AVAILABLE?
	ORA	A	;0=NO
	JZ	OSB1	;USE INTERNAL PATH
	LHLD	EPADR	;PT TO EXTERNAL PATH
OSB1:
	PUSH	H	;SAVE PATH PTR
	CALL	FNDFILE	;LOOK FOR FILE ALONG PATH AND SAY IF IT IS FOUND
	POP	H	;GET PATH PTR
	JNZ	READSB
;
;  ZEX FILE NOT FOUND -- SET UP TO READ SUB FILE
;
	PUSH	H	;SAVE PATH PTR
	CALL	GETUD	;RESTORE USER/DISK
	LXI	D,DFCB+9	;SET TYPE TO SUB
	LXI	H,SUBNAM
	MVI	B,3
	CALL	MOVE
	XRA	A	;ZERO CR FIELD
	STA	DFCB+32
	POP	H	;PT TO PATH
	LXI	D,DFCB	;PT TO FCB
	CALL	FNDFILE	;LOOK FOR FILE
	JNZ	READSB
RSBERR:
	CALL	GETUD	;RESTORE USER/DISK
	LXI	H,NOSBF2
	LXI	D,DFCB+1
	MVI	B,8	;NAME LENGTH
	CALL	MOVEFN	;MOVE FILE NAME
	MVI	B,3	;TYPE LENGTH
	MVI	M,'.'
	INX	H
	LXI	D,DFCB+9;FILE TYPE POINTER
	CALL	MOVEFN	;MOVE FILE TYPE
	MVI	M,'$'	;END TERMINATER
	JMP	NOSUB
*
*  FNDFILE -- LOOK FOR FILE ALONG ZCPR3 PATH
*  INPUT PARAMETERS:  HL = BASE ADDRESS OF PATH, DE = PTR TO FCB OF FILE
*  OUTPUT PARAMETERS:  A=0 AND ZERO FLAG SET IF NOT FOUND, NZ IF FOUND
*
FNDFILE:
	SHLD	PATH		;SAVE PATH BASE ADDRESS
	MVI	C,17		;SEARCH FOR FIRST
	CALL	BENTRY		;LOOK FOR FILE
	INR	A		;SET FLAG
	JNZ	FF5		;FOUND IT -- RETURN FOUND FLAG
	XCHG			;HL=FCB PTR
	SHLD	FCBPTR		;SAVE IT
	LHLD	PATH		;PT TO PATH FOR FAILURE POSSIBILITY
	MVI	C,32		;GET CURRENT USER
	MVI	E,0FFH
	CALL	BENTRY
	STA	TMPUSR		;SAVE IT FOR LATER
;
; MAIN SEARCH LOOP
;
FF1:
	MOV	A,M		;GET DRIVE
	ANI	7FH		;MASK MSB
	ORA	A		;0=DONE=COMMAND NOT FOUND
	JNZ	FF2		;NO ERROR ABORT?
;
; FILE NOT FOUND ERROR
;
	XRA	A		;ZERO FLAG MEANS NOT FOUND
	RET
;
; LOOK FOR COMMAND IN DIRECTORY PTED TO BY HL; DRIVE IN A
;
FF2:
	MOV	E,A		;DISK IN E
	CPI	'$'		;CURRENT DISK?
	JNZ	FF3		;SKIP DEFAULT DRIVE SELECTION IF SO
	LDA	BDISK		;GET DEFAULT USER/DISK
	ANI	0FH		;MASK FOR DEFAULT DISK
	INR	A		;PREP FOR FOLLOWING DCR A
	MOV	E,A		;DISK NUMBER IN E
FF3:
	DCR	E		;ADJUST PATH 1 TO 0 FOR A, ETC
	MVI	C,14		;SELECT DISK FCT
	CALL	BENTRY		;SELECT DRIVE
	INX	H		;PT TO USER NUMBER
	MOV	A,M		;GET USER NUMBER
	ANI	7FH		;MASK OUT MSB
	INX	H		;PT TO NEXT ENTRY IN PATH
	PUSH	H		;SAVE PTR
	MOV	E,A		;SAVE IN E
	CPI	'$'		;MATCH?
	JNZ	FF4		;DO NOT SELECT CURRENT USER IF SO
	LDA	TMPUSR		;GET ORIGINAL USER NUMBER
	MOV	E,A		;SELECT USER
FF4:
	MVI	C,32
	CALL	BENTRY
	LHLD	FCBPTR		;GET PTR TO FCB
	XCHG			;... IN DE
	MVI	C,17		;SEARCH FOR FIRST
	CALL	BENTRY		;LOOK FOR FILE
	POP	H		;GET PTR TO NEXT PATH ENTRY
	INR	A		;SET FLAG
	JZ	FF1		;CONTINUE PATH SEARCH IF SEARCH FAILED
;
; FILE FOUND -- PERFORM SYSTEM TEST AND PROCEED IF APPROVED
;
FF5:
	MVI	A,0FFH		;SET OK RETURN
	ORA	A
	RET

;
;  BDOS ROUTINE
;
BENTRY:
	PUSH	H	;SAVE REGS
	PUSH	D
	PUSH	B
	CALL	BDOS
	POP	B	;GET REGS
	POP	D
	POP	H
	RET

* BUFFERS
FCBPTR:
	DS	2	;POINTER TO FCB FOR FILE SEARCH
TMPUSR:
	DS	1	;CURRENT USER NUMBER
PATH:
	DS	2	;BASE ADDRESS OF PATH
;
;  PUTUD -- SAVE AWAY CURRENT USER/DISK
;  GETUD -- RESTORE CURRENT USER/DISK
;
PUTUD:
	MVI	E,0FFH	;GET CURRENT USER
	MVI	C,32	;BDOS
	CALL	BDOS
	STA	CUSER	;SAVE CURRENT USER AWAY
	MVI	C,25	;GET CURRENT DISK
	CALL	BDOS
	STA	CDISK
	RET
GETUD:
	LDA	CDISK	;GET CURRENT DISK
	MOV	E,A	;... IN E
	MVI	C,14	;SELECT DISK
	CALL	BDOS
	LDA	CUSER	;GET CURRENT USER
	MOV	E,A	;... IN E
	MVI	C,32	;SELECT USER
	CALL	BDOS
	RET
CDISK:
	DS	1	;CURRENT DISK NUMBER
CUSER:
	DS	1	;CURRENT USER NUMBER
;
;  OPEN AND READ SUB FILE
;
READSB:
	MVI	C,15	;OPEN FILE
	CALL	BDOS	;BDOS
	INR	A	;ERROR?
	JZ	RSBERR
;
;  READ IN AND STORE SUB FILE
;
READTX:
	LHLD	INBUF	;GET PTR TO NEXT BYTE
	XCHG		;SET PTR IN DE
	LXI	H,80H	;GET SECTOR OFFSET
	DAD	D	;HL PTS TO FOLLOWING BLOCK TO BE READ, DE PTS TO
	SHLD	INBUF	;  BLOCK TO READ; SAVE PTR TO FOLLOWING BLOCK
	MVI	C,26	;SET DMA ADDRESS
	CALL	BDOS
	LXI	D,DFCB
	MVI	C,20	;READ SEQUENTIAL
	CALL	BDOS
	ORA	A
	JZ	READTX	;READ COMPLETE .SUB FILE
	CALL	GETUD	;RESTORE CURRENT USER/DISK
	LHLD	INBUF	;MAKE SURE BUFFER'S TERMINATED
	LXI	D,-100H	;PT TO FIRST BYTE OF LAST BLOCK READ
	DAD	D
	MVI	B,80H	;LOOK AT AT MOST 80H BYTES
SKIP1A:
	MOV	A,M	;GET BYTE
	CPI	CTRLZ	;EOF?
	JZ	SKIP1B
	INX	H	;PT TO NEXT
	DCR	B	;COUNT DOWN
	JNZ	SKIP1A
;  HL NOW POINTS TO AFTER LAST VALID CHAR IN FILE
SKIP1B:
	SHLD	INBUF	;SET PTR
	RET		;DONE WITH NO ERROR

;
;  THIS PART OF THE CODE STORES THE REST OF THE COMMAND LINE AS PART OF THE
;  COMMAND FILE FOR ZCPR3; ON ENTRY, HL PTS TO NEXT AVAILABLE BYTE
;
ZMCL:
	XCHG		;BUFFER PTED TO BY DE
	LHLD	MCADR	;GET BASE ADDRESS OF MULTIPLE COMMAND LINE
	MOV	A,M	;GET LOW
	INX	H
	MOV	H,M	;GET HIGH
	MOV	L,A	;HL PTS TO NEXT CHAR IN MULTIPLE COMMAND LINE
	XCHG		;DE PTS TO NEXT CHAR IN COMMAND LINE, HL PTS TO BUF END
	LDA	MCAVAIL	;MULTIPLE COMMANDS ENABLED?
	ORA	A	;0=NO
	JZ	ENDSTR	;TERMINATE FILE; HL PTS TO NEXT BYTE
	LDAX	D	;GET FIRST BYTE
	MOV	B,A	;SAVE FIRST BYTE IN B
	XRA	A	;A=0
	STAX	D	;CLEAR COMMAND LINE
	INX	D	;PT TO NEXT BYTE
	MOV	A,B	;GET FIRST BYTE
	CPI	';'	;SEPARATION CHAR?
	JNZ	CMCMD1	;PROCESS IF NOT
;
;  LOOP TO STORE REST OF MULTIPLE COMMAND LINE INTO LOADED FILE
;
CMCMD:
	LDAX	D	;GET BYTE FROM LINE
CMCMD1:
	ORA	A	;EOL IF ZERO
	JZ	CMEND	;READ IN FILE; HL PTS TO NEXT AVAILABLE BYTE
	MOV	M,A	;STORE BYTE
	INX	H	;PT TO NEXT
	INX	D
	JMP	CMCMD
CMEND:
	MVI	M,CR	;STORE <CR> <LF>
	INX	H
	MVI	M,LF
	INX	H	;PT TO NEXT AVAILABLE BYTE
;
;  MARK END OF BUFFER AND CONTINUE
;
ENDSTR:
	MVI	M,1AH	;EOF CHARACTER
	SHLD	ENDBUF	;EOB ADDRESS
	MOV	A,L
	SUI	LOW BEGREL+1	;SEE IF BUFFER'S EMPTY
	MOV	A,H
	SBI	HIGH BEGREL
	JC	BUFLOW
	RET
;
;  COPY AND PROCESS COMMAND LINES, PLACING FINAL COMMAND LINE FORM UNDER ZEX
;    RETURN WITH HL PTING TO NEXT AVAILABLE BYTE IN MEMORY BUFFER UNDER ZEX
;
ZLINES:
	XRA	A
	STA	IMFLG1
	STA	IMFLG2
	STA	PRTFLG
	STA	OUTCNT
	LXI	H,1
	SHLD	LINES	;SET LINE COUNT
	LHLD	OUTBUF	;PT TO BYTE JUST BELOW LOADED ZEX
	SHLD	OUTLNE
	SHLD	BUFSTR
	LXI	D,BEGREL	;PT TO FIRST BYTE OF COMMAND BUFFER
;
;  MAIN COPY LOOP TO COPY BUFFER AT BEGREL TO JUST UNDER ZEX WITH PROCESSING
;
MOVSTR:
	LDAX	D	;GET NEXT COMMAND BYTE
	INX	D	;PT TO FOLLOWING
	ANI	7FH	;MAKE SURE NO PARITY
	CPI	LF	;NEW LINE?
	JNZ	MOVST0
;
;  NEW LINE -- DON'T STORE <LF> AND INCREMENT LINE COUNT
;
MOVSTX:
	CALL	INCR	;INCREMENT LINE COUNT
	JMP	MOVSTR	;CONTINUE
;
;  BEGIN CHARACTER PROCESSING
;    A CONTAINS CHAR, DE PTS TO BYTE AFTER CHAR, HL PTS TO NEXT BUFFER POS
;
MOVST0:
	CPI	1AH	;END OF INPUT?
	RZ		;DONE IF SO
	CPI	'|'	;CARRIAGE RETURN?
	JNZ	MOVST1	;NOPE
;
;  PROCESS CARRIAGE RETURN FORM (|)
;
	PUSH	D	;SAVE OLD POINTER
	INX	D	;LOOK FOR EOF AFTER | (PT TO LF)
	INX	D	;PT TO POSSIBLE EOF
	LDAX	D	;GET PRESENT LOCATION+2
	POP	D	;GET OLD POINTER
	CPI	1AH	;END OF BUFFER
	RZ		;END, SO NO FOLLOWING <CR>
	MVI	A,CR	;MAKE CHAR A <CR>
	CALL	INCR	;INCREMENT LINES FOR ERRORS
	JMP	MOVST4	;STORE <CR> IN A
;
;  CHECK FOR NON-CR FORMS
;    AT THIS POINT, DE PTS TO NEXT CHAR IN LINE AND HL PTS TO NEXT
;    BYTE IN BUFFER (MOVING DOWN)
;
MOVST1:
	MOV	C,A	;SAVE CHAR IN C
	LDA	IMFLG1
	CPI	IMON	;IMMEDIATE MODE ON ?
	MOV	A,C	;GET CHAR BACK
	JZ	MOVST2	;YES..SKIP ZEX COMMENT PROCESSING
	CPI	';'	;FIRST ';'?
	JZ	EXCOMM	;PROCESS POSSIBLE ZEX COMMENT
MOVST2:
	CPI	'^'	;CONTROL CHAR?
	JZ	MOVST5	;CONVERT CONTROL CHARACTERS
	CPI	'$'	;PARAMETER OR CONTROL CHAR?
	CZ	GTPARM	;SUBSTITUTE COMMAND PARAMETER OR CONTROL CHAR.
MOVST3:
	STA	LCHR	;SAVE LAST CHAR ENTERED
	CPI	CR	;=CR?
	JNZ	MOVST4
	MOV	C,A	;SAVE CHAR TEMPORARILY
	LDA	OUTCNT	;GET CHAR OUTPUT FLAG
	ORA	A	;ANY CHAR?
	MOV	A,C
	JZ	MOVSTR	;NO..USE INPUT CR ONLY IF OTHER NON-CONTROL
;			     CHARACTERS IN CURRENT LINE
;  PLACE CHAR IN BUFFER
;    CHAR IN A, HL PTS TO BUFFER LOC
;
MOVST4:
	CALL	CHRSTR	;ADD TO BUFFER
	CALL	CNTINC	;INCREMENT COUNT
	JMP	MOVSTR
;
;  PREFIX WAS AN UPARROW (^), SO PROCESS CONTROL CHARS
;
MOVST5:
	CALL	GETCMD	;VALIDATE CONTROL CHARACTERS
	CPI	':'
	JZ	REXC	;RE-EXECUTE
	CPI	'?'
	JZ	GCRW	;CR WAIT
	CPI	'/'
	JZ	GCRBW	;RING BELL AND WAIT FOR <CR>
	CPI	'"'
	JZ	UISET	;USER INPUT
	CPI	'*'
	JZ	GRNG	;CONTINUALLY RING BELL WHILE WAITING FOR <CR>
	CPI	'|'
	JZ	GCRLF	;CR,LF GENERATION
	CPI	'$'
	JZ	PRMDEF	;DEFAULT PARAMETERS' LINE
	CPI	'.'
	JZ	PRTSUP	;PRINT SUPPRESS TOGGLE
	CPI	'#'
	JZ	MSGSUP	;MESSAGE SUPPRESS TOGGLE
	CPI	'<'
	JZ	IMPRTY	;IMMEDIATE MODE START
	CPI	'>'
	JZ	IMPRTN	;IMMEDIATE MODE STOP
	CPI	'&'
	JZ	IFPSUP	;PRINT SUPPRESS DURING FALSE IF
	JMP	MOVST3	;OTHER CONTROL CODES
;
IFPSUP:
	MVI	A,IPS	;CONVERT '^&' TO IF PRINT SUPPRESS FLAG
	JMP	MOVST3
;
REXC:
	MVI	A,REXEC	;CONVERT '^:' TO RE-EXECUTE FLAG
	JMP	MOVST3
;
GCRW:
	MVI	A,CRWAIT	;CONVERT '^?' TO CRWAIT FLAG
	JMP	MOVST3
;
GCRBW:
	MVI	A,CRBWAIT	;CONVERT '^/' TO CRBWAIT FLAG
	JMP	MOVST3
;
;  ALLOW USER INPUT FROM NOW ON, BUT FIRST SKIP OUT REST OF LINE
;
UISET:
	LDAX	D		;GET NEXT CHAR
	ANI	7FH		;MASK IT
	CPI	LF		;DONE?
	JZ	UISET1
	CPI	1AH		;EOF?
	JZ	UISET1
	INX	D		;PT TO NEXT CHAR
	JMP	UISET		;CONTINUE SKIPPING
UISET1:
	MVI	A,UICH		;CONTROL CHAR
	JMP	MOVST3
;
GRNG:
	MVI	A,RNG		;CONVERT '^*' TO RNG FLAG
	JMP	MOVST3
;
GCRLF:
	MVI	A,CR	;GENERATE CR & LF
	CALL	CHRSTR
	MVI	A,LF
	CALL	CHRSTR
	STA	LCHR
	JMP	MOVSTR
;
PRMDEF:
	PUSH	H
	LXI	H,PRMDFP
	PUSH	H
	LXI	B,PRMDFL
	XRA	A
	CALL	FILL	;CLEAR PTR TABLE
	POP	H
	MVI	A,PRMDFL/2
	STA	PRMMAX	;HIGHEST PARAMETER #
	CALL	PARMS	;BUILD DEFAULT PARAMETERS PTRS
	POP	H
	INX	D	;SKIP CR
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE AT EOL
;
;  CHECK TO SEE IF PREVIOUS CHAR WAS ALSO A ; AND FLUSH AS ZEX COMMENT IF SO
;
EXCOMM:
	PUSH	H
	LXI	H,LCHR	;PT TO PREVIOUS CHAR
	CMP	M	; DOUBLE ;?
	MOV	M,A	;STORE CURRENT CHAR AS PREVIOUS CHAR
	POP	H
	JNZ	MOVST3	;NO...CONTINUE
	MOV	C,A	;SAVE CHAR
	LDA	PRTFLG
	CPI	PSUP
	MOV	A,C
	JZ	MOVST3	;PRINT SUPPRESS
	LDA	IMFLG1
	CPI	IMON
	MOV	A,C
	JZ	MOVST3	;IMMEDIATE MODE
	INX	H	;YES..IGNORE PREVIOUS ;
	PUSH	H
	LXI	H,LCHR
	LDA	OUTCNT
	DCR	A	;DROP 1 CHAR.
	STA	OUTCNT
EXCOML:
	LDAX	D	;IGNORE CHARACTERS UNTIL EOF OR LF
	INX	D
	CPI	1AH	;EOF
	JZ	EXCOMX
	CPI	LF	;LINE FEED
	JNZ	EXCOML
	MOV	M,A
	LDA	OUTCNT
	ORA	A	;ANY CHAR. ON THIS LINE?
	JZ	EXCOM2	;NO...SKIP CR
EXCOM1:
	POP	H	;YES..FORCE CR
	MVI	A,CR
	CALL	CHRSTR
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE
;
EXCOM2:
	POP	H
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE
;
EXCOMX:
	POP	H
	RET		;RETURN TO MAIN FLOW, WITH HL PTING TO NEXT BYTE
;
MSGSUP:
	MVI	A,MSUP	;CONVERT '^#' TO MESSAGE SUPPRESS FLAG
	JMP	MOVST3
;
PRTSUP:
	MVI	A,PSUP	;CONVERT '^.' TO PRINT SUPPRESS FLAG
	PUSH	H
	LXI	H,PRTFLG
	CMP	M	;ALREADY ON?
	JNZ	PRTSST	;NO...SET FLAG
	XRA	A	;YES..CLEAR FLAG
PRTSST:
	MOV	M,A	;SET/RESET FLAG
	POP	H
	MVI	A,PSUP
	JMP	MOVST3
;
IMPRTY:
	MVI	A,IMON	;CONVERT '^<' TO IMMEDIATE MODE START
	STA	LCHR
	PUSH	H
	LXI	H,IMFLG1
	CMP	M	;ALREADY ON?
	POP	H
	JZ	MOVSTR	;YES..
	STA	IMFLG1
	STA	IMFLG2
	JMP	MOVST3	;NO...
;
IMPRTN:
	MVI	A,IMOFF ;CONVERT '^>' TO IMMEDIATE MODE STOP
	STA	LCHR
	PUSH	H
	LXI	H,IMFLG2
	CMP	M	;ALREADY OFF?
	POP	H
	JZ	MOVSTR	;YES..
	STA	IMFLG2
	STA	IMFLG1
	JMP	MOVST3	;NO...
;
;  PLACE CHAR IN BUFFER; A=CHAR, HL PTS TO BUFFER LOC
;
CHRSTR:
	PUSH	PSW	;CHECK FOR INPUT/ZEX BUFFER OVERLAP
	PUSH	D
	PUSH	H
	LHLD	ENDBUF
	XCHG
	POP	H
	MOV	A,L
	CMP	E
	JNZ	CHRSTX	;LSB<>
	MOV	A,H
	CMP	D
	JZ	OVERL	;MSB=, OVERLAP WILL OCCUR/ABORT ZEX
;
; ADD CHAR TO ZEX'S BUFFER
;
CHRSTX:
	POP	D	;ADD CHAR. TO ZEX'S BUFFER
	POP	PSW
	MOV	M,A	;STORE CHAR
	DCX	H	;PT TO NEXT LOCATION (MOVING DOWN)
	RET
;
;  CHECK TO SEE IF ZEX IS ALREADY ACTIVE, AND ABORT IF SO
;
ZEXACTV:
	MVI	B,08H	;MESSAGE OFFSET
	CALL	MLDA	;GET VALUE IN A
	ORA	A	;0 IF NO
	RZ
	LXI	D,ZEXACT
	CALL	PRINT	;ZEX ALREADY PRESENT
;
;  ABORT AND RETURN TO ZCPR3
;
CCPRET:
	LHLD	CCPSTK	;RESTORE STACK
	SPHL
	RET		;RETURN TO CCP

;
;  ZCPR3 MESSAGE BUFFER ACCESS ROUTINES
;

;
;  GETZ3MSG RETURNS HL POINTING TO DESIRED MESSAGE (OFFSET IN B)
;
GETZ3MSG:
	PUSH	D	;SAVE DE
	LHLD	Z3MSGA	;GET ADDRESS OF MESSAGES
	MOV	E,B	;GET OFFSET
	MVI	D,0
	DAD	D	;HL PTS TO MESSAGE OF INTEREST
	POP	D
	RET

MSHLD:
	PUSH	D	;SAVE DE
	XCHG
	CALL	GETZ3MSG	;MAKE HL PT TO MESSAGE
	MOV	M,E	;STORE LOW
	INX	H
	MOV	M,D	;STORE HIGH
	XCHG		;RESTORE HL
	POP	D	;RESTORE DE
	RET

MSTA:
	PUSH	H	;SAVE HL
	CALL	GETZ3MSG	;PT TO MESSAGE WITH HL
	MOV	M,A	;STORE MESSAGE
	POP	H
	RET

MLDA:
	PUSH	H	;SAVE HL
	CALL	GETZ3MSG	;PT TO MESSAGE WITH HL
	MOV	A,M	;GET MESSAGE
	POP	H
	RET

;
;  END OF Z3MSG ACCESS ROUTINES
;

;
;	ERROR EXITS
;
GETERR:
	LXI	D,CMDER ;CONTROL CHARACTER INVALID
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
NUMERR:
	LXI	D,NONUM	;EXCESSIVE NUMBER
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
PRMERR:
	LXI	D,PMERR
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
PRMTOO:
	LXI	D,TOOARG;TOO MANY PARAMETER ARGUMENTS
	CALL	PRINT
	LHLD	ERRLNE
	CALL	EPRT	;PRINT PARAMETER LINE
	JMP	CCPRET
;
BUFLOW:
	LXI	D,BUFMTY;TEXT BUFFER EMPTY
	CALL	PRINT
	JMP	CCPRET
;
NOSUB:
	LXI	D,NOSBF1;.SUB FILE NOT FOUND
	CALL	PRINT
	LXI	D,NOTHER
	CALL	PRINT
	JMP	CCPRET
;
OVERL:
	LXI	D,OVERLP;INPUT/ZEX BUFFER OVERLAP
	CALL	PRINT
	JMP	LINE
;
;	SUBROUTINES
;
;	CONTROL CODES 0-1FH
;	WITH SUPPORT FOR $ . # < >
;
GETCMD:
	LDAX	D	;GET NEXT CHARACTER
	INX	D	;INCREMENT POINTER
	CPI	'|'
	RZ		;CR,LF GENERATION
	CPI	'a'-1	;LOWERCASE?
	JC	GETUPR	;NOPE
	CPI	'z'+1	;a-z?
	JNC	GETERR	;NOPE
	sui	'a'-'A' ;GET TO UPPERCASE
GETUPR:
	CPI	'@'	;0-1FH CONTROL CODE?
	JNC	GETCC
	CPI	':'
	RZ		;RE-EXECUTE
	CPI	'?'
	RZ		;CR WAIT
	CPI	'/'
	RZ		;CR WAIT AND RING BELL
	CPI	'*'
	RZ		;RING BELL
	CPI	'"'
	RZ		;USER INPUT
	CPI	'$'
	RZ		;DEFAULT PARAMETERS' LINE
	CPI	'.'
	RZ		;PRINT SUPPRESS TOGGLE
	CPI	'#'
	RZ		;MESSAGE SUPPRESS TOGGLE
	CPI	'<'
	RZ		;IMMEDIATE MODE START
	CPI	'>'
	RZ		;IMMEDIATE MODE STOP
	CPI	'&'
	RZ		;FALSE IF PRINT SUPPRESS
	JMP	GETERR
GETCC:
	SUI	'@'	;GET CONTROL CODE
	RNC
	JMP	GETERR
;
;  EXTRACT PARAMETER ELEMENT WHOSE $N SPECIFICATION IS POINTED TO BY DE
;    DE PTS TO CHAR AFTER THE $
;    BUFFER TO PLACE RESULTING PARAMETER IS PTED TO BY HL
;
GTPARM:
	LDAX	D	;GET CHAR AFTER THE $
	INX	D	;PT TO NEXT CHAR
	CPI	'$'	;IF DOUBLE $, THEN STORE AS $
	RZ
	CPI	'^'	;UP ARROW
	RZ
	CPI	'|'	;CARRIAGE RETURN
	RZ
	CPI	'1'	;CHECK FOR VALID DIGIT (1-9)
	JC	PRMERR
	CPI	'9'+1	;RANGE ERROR?
	JNC	PRMERR
	SUI	'1'	;GET ACTUAL # (ZERO RELATIVE)
	ADD	A	;DOUBLE FOR OFFSET
	STA	PRMNUM
	PUSH	D	;SAVE PTRS
	PUSH	H
	LXI	H,PRMPNT	;PT TO PARAMETER PTR TABLE
	CPI	PRMPNL-1	;PARAMETER NUMBER WITHIN RANGE?
	JNC	NOPARM	;> HIGHEST #
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	E,M	;GET PARAMETER POINTER
	INX	H
	MOV	D,M
	POP	H	;RESTORE PTR TO NEXT BYTE IN OUTPUT BUFFER BELOW ZEX
	MOV	A,E	;ANY PARAM?
	ORA	D
	JZ	NOPARM	;NO PARAMETER PRESENT, TRY DEFAULTS
;
;  MOVE PARAMETER PTED TO BY DE INTO BUFFER BELOW ZEX, 1ST BYTE PTED TO BY HL
;
MOVPRM:
	LDAX	D	;GET PARAMETER CHAR
	INX	D	;PT TO NEXT
	ORA	A	;DONE?
	JZ	ENDPAR
	CALL	CHRSTR	;STORE CHARS
	JMP	MOVPRM
;
;  PARAMETER PLACED IN MEMORY -- CONTINUE
;
ENDPAR:
	POP	D	;GET PTR TO NEXT CHAR IN LINE
	POP	PSW	;CLEAR STACK
	JMP	MOVSTR	;RESUME PROCESSING
;
;  NO PARAMETER PTED TO
;
NOPARM:
	PUSH	H	;SAVE PTR TO NEXT BYTE IN BUFFER BELOW ZEX
	LXI	H,PRMDFP	;TRY DEFAULT PARAMETERS
	LDA	PRMNUM
	CPI	PRMDFL-1
	JNC	NUMERR	;> HIGHEST #
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	E,M	;GET PARAMETER POINTER
	INX	H
	MOV	D,M
	POP	H
	MOV	A,E
	ORA	D
	JNZ	MOVPRM	;MOVE PARAMETER INTO BUFFER
	JMP	ENDPAR	;RESUME WITH NO PARAMETER
;
MOVEFN:
	LDAX	D
	CPI	' '	;SEE IF SPACE
	RZ
	MOV	M,A
	INX	D	;INCREMENT POINTERS
	INX	H
	DCR	B
	JNZ	MOVEFN
	RET
;
;  INCREMENT LINE COUNT, AND AFFECT ONLY HL (MUST NOT AFFECT A)
;
INCR:
	PUSH	H	;SAVE OUTPUT POINTER
	LHLD	LINES
	INX	H	;INCREMENT LINE COUNTER
	SHLD	LINES
	LXI	H,LCHR	;CLEAR LAST CHARACTER
	MVI	M,0
	LXI	H,OUTCNT;CLEAR CHARACTER COUNT
	MVI	M,0
	MOV	L,E	;DE=HL
	MOV	H,D
	SHLD	BEGLIN
	POP	H
	SHLD	OUTLNE	;SAVE NEW OUTPUT LINE
	RET
;
CNTINC:
	CPI	' '	;CONTROL CHARACTER?
	RC		;YES..
	CPI	UICH	;USER INPUT CHAR?
	JZ	CNTIN1
	ANI	80H	;SPECIAL CONTROL?
	RNZ		;YES..
	LDA	PRTFLG
	CPI	PSUP	;PRINT SUPPRESS FLAG?
	RZ		;YES..
	LDA	IMFLG1
	CPI	IMON	;IMMEDIATE MODE?
	RZ		;YES..
CNTIN1:
	LDA	OUTCNT
	INR	A
	STA	OUTCNT
	RET
;
PRINT:
	MVI	C,9	;PRINT STRING AT (DE)
	JMP	BDOS
;
EPRT:
	MOV	A,M	;PRINT PARAMETER LINE AT (HL)
	CPI	CR
	RZ
	CPI	0
	JNZ	EPRT1
	MVI	A,' '
EPRT1:
	INX	H
	PUSH	H
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	POP	H
	JMP	EPRT
;
CRLF:
	LXI	D,CRLFS ;PRINT CR/LF
	JMP	PRINT
;
LINE:
	LXI	D,LINEM ;PRINT LINE # AND LINE IN ERROR AND EXIT
	CALL	PRINT
	LHLD	LINES
	CALL	DECOUT	;PRINT LINE #
	CALL	CRLF
	LHLD	BEGLIN
	PUSH	H	;SAVE BEGGING POINTER
FINDCR:
	MOV	A,M
	INX	H
	CPI	1AH	;END OF BUFFER
	JZ	FOUND
	CPI	CR
	JNZ	FINDCR
FOUND:
	MVI	M,0	;END OF STRING
	POP	H	;START OF STRING
	CALL	PRNTHL	;PRINT BAD LINE
	JMP	CCPRET	;THATS ALL FOLKS
;
PRNTHL:
	MOV	A,M	;PRINT LINE AT (HL)
	INX	H
	ORA	A
	RZ
	MOV	E,A
	PUSH	H	;SAVE POINTER
	CALL	OUTCHR
	POP	H	;GET POINTER BACK
	JMP	PRNTHL
;
OUTCHR:
	MVI	C,2	;PRINT CHARACTER IN E
	JMP	BDOS
;
DECOUT:
	PUSH	H	;PRINT DECIMAL LINE NUMBER
	PUSH	D
	PUSH	B
	LXI	B,-10	;RADIX FOR CONVERSION
	LXI	D,-1	;THIS BECOMES NO DIVIDED BY RADIX
DX:
	DAD	B	;SUBTRACT 10
	INX	D
	JC	DX
	LXI	B,10
	DAD	B	;ADD RADIX BACK IN ONCE
	XCHG
	MOV	A,H
	ORA	L	;TEST FOR ZERO
	CNZ	DECOUT	;RECURSIVE CALL
	MOV	A,E
	ADI	'0'	;CONVERT FROM BCD TO HEX
	MOV	E,A	;TO E FOR OUTPUT
	MVI	C,2
	CALL	BDOS
	POP	B	;RESTORE REGISTERS
	POP	D
	POP	H
	RET
;
MOVE:
	MOV	A,M	;MOVE STRING AT (HL) TO (DE) FOR LENGTH IN B
	INX	H
	STAX	D
	INX	D
	DCR	B
	JNZ	MOVE
	RET
;
FILL:
	PUSH	D	; FILL STORAGE AT (HL) WITH CHARACTER IN A
	MOV	E,A	; FOR LENGTH IN BC
	MOV	A,B
	ORA	C
	MOV	A,E
	POP	D
	RZ
	DCX	B
	MOV	M,A
	INX	H
	JMP	FILL
;
;	WORKING STORAGE AREA
;
SUBNAM:
	DB	'SUB'
ZEXNAM:
	DB	'ZEX'
LINEM:
	DB	' Error Line # $'
ZEXACT:
	DB	CR,LF,' ZEX Already Present$'
BUFMTY:
	DB	CR,LF,'Text Buffer Empty$'
OVERLP:
	DB	CR,LF,'Input/ZEX Buffer Overlap$'
NONUM:
	DB	CR,LF,'Parameter Number out of range$'
NOPRM:
	DB	CR,LF,'No Parameter or Default Parameter$'
PMERR:
	DB	CR,LF,'Parameter$'
NOSBF1:
	DB	CR,LF,'File '
NOSBF2:
	DB	'filename.typ$'
NOTHER:
	DB	' not there$'
CMDER:
	DB	CR,LF,'Control character$'
TOOARG:
	DB	CR,LF,'Too many arguments - $'
SIGNON:
	DB	'ZEX, Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0','$'
CRLFS:
	DB	CR,LF,'$'
;
	DS	80	;STACK SPACE
CCPSTK:
	DW	0	;CCP STACK PTR
IMFLG1:
	DB	0	;=IMON ENCOUNTERED
IMFLG2:
	DB	0	;=IMOFF ENCOUNTERED
PRTFLG:
	DB	0	;=PSUP ON
LCHR:
	DB	0	;LAST CHARACTER READ
PRMMAX:
	DB	0	;HIGHEST PARAMETER #
PRMNUM:
	DB	0	;CURRENT $<1-9> NUMBER * 2 (ZERO RELATIVE)
ERRLNE:
	DW	0
BITMAP:
	DB	0	;PRESENT OFFSET BIT'S
COUNT:
	DB	0FFH	;PRESENT OFFSET BIT COUNT
BEGLIN:
	DW	BEGREL	;BEGINNING OF OLD LINE POINTER
LINES:
	DW	1
INBUF:
	DW	BEGREL
ENDBUF:
	DW	0	;END OF INPUT BUFFER
OUTCNT:
	DB	0
OUTLNE:
	DW	0
RELSTRT:
	DW	0
OUTBUF:
	DW	0
BUFSTR:
	DW	0
RELOCL:
	DW	0	;LENGTH OF RELOC PROGRAM (FILLED IN BY SID)
PRMDFP: 		;DEFAULT PARAMETER PTRS
	REPT	9
	DW	0
	ENDM
PRMDFL	EQU	$-PRMDFP
PRMDMY:
	DW	0	;DUMMY PARAMETER FOR .SUB FILE SPEC.
PRMPNT: 		;COMMAND LINE PARAMETER PTRS
	REPT	9
	DW	0
	ENDM
PRMPNL	EQU	$-PRMPNT
PATCH:			;PATCH AREA
	REPT	32
	DB	'p'
	ENDM
	REPT	30
	DW	0
	ENDM
;
;	INSURE 8 BYTE BOUNDARY FOR REL.UTL(RELS.UTL)
;
?PLOC	SET	$
	IF	(?PLOC MOD 8) GT 0
?PLOC	SET	(?PLOC AND 0FFF8H)+8 ;GET NEXT 8 BYTE BOUNDARY
	ORG	?PLOC
	ENDIF
;
BEGREL:
	DS	0	;RELOC PROGRAM STARTS HERE (ALSO USED AS BUFFER)
;
	ENDIF
;
;	END OF ZEX INITIATOR CODE SEGMENT
;
$-PRINT
	IF NOT	ZEXBASE
$+PRINT
;
;	START OF ZEX RELOCATED CODE SEGMENT
;		HL PTS TO MULTIPLE COMMAND BUFFER
;		 OR HL=0 IF NO MULTIPLE COMMANDS
;
	ORG	ZEXREL
;
ZEX:
	SHLD	EXMBASE	;SAVE ADDRESS OF MCL BUFFER
	XCHG		;HL=ADDRESS OF ZCPR3 MESSAGES
	SHLD	Z3MSGA	;SAVE ADDRESS
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MLHLD	;GET ADDRESS OF NEXT CHAR
	MOV	A,M	;GET 1ST CHAR
	CPI	MSUP	;1ST CHAR=MESSAGE SUPPRESS?
	JNZ	ZEX1	;NO...
	DCX	H	;YES..SKIP CHARACTER
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;SET PTR TO NEXT CHAR
	STA	MSUPFL	;SET INITIAL FLAG
ZEX1:
	LXI	SP,MEMTOP
	LHLD	BDOS+1	;GET WARM JUMP FOR STANDARD CCP
	SHLD	MEMTOP	;SET PTR TO TOP OF MEMORY
	INX	H
	MOV	E,M	;DE = ADDRESS OF ACTUAL BDOS
	INX	H
	MOV	D,M
	XCHG		;HL PTS TO ACTUAL BDOS
	MOV	A,H	;SUBTRACT 8 FOR CCP ENTRY POINT
	SUI	8
	MOV	H,A
	MVI	L,3	;SET UP FOR WARM CCP JUMP
	SHLD	CCPJMP
	LHLD	WARM+1	;SAVE WARM BOOT ADDRESS
	SHLD	WARMPT
	LXI	D,BSWARM	;SAVE OLD BIOS JUMPS
	MVI	B,12
	CALL	MOVE	;MOVE BIOS JUMPS
	LHLD	WARMPT
	XCHG
	LXI	H,LOCJMP	;STORE NEW BIOS JUMPS
	MVI	B,12
	CALL	MOVE	;MOVE NEW BIOS JUMPS TO BIOS AREA
;
;	ZEX RUNTIME BIOS INTERCEPT ROUTINES
;
NWARM:
	LXI	SP,MEMTOP
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MLHLD	;GET ADDRESS OF NEXT CHAR
	MOV	A,M
	CPI	0FFH	;TEST IT
	JZ	WARMX	;WARM RETURN
	LHLD	WARMPT	;SET WARM BOOT ADDRESS
	SHLD	WARM+1
	LHLD	MEMTOP	;SET BDOS ENTRY ADDRESS
	SHLD	BDOS+1
	LXI	D,BUFF	;DMA ADDRESS
	MVI	C,26	;SET DMA
	CALL	BDOS
	LDA	BDISK
	MOV	C,A
	LHLD	CCPJMP
	PCHL		;GOTO CONSOLE PROCESSOR
;
;	JMP TABLE TO OVERLAY BIOS WITH NEW ZEX-BASED JUMPS
;
LOCJMP:
	JMP	NWARM	;WARM
	JMP	BCONST	;CONST
	JMP	NCONIN	;CONIN
	JMP	NCONOT	;CONOT
;
;  ZCPR3 MESSAGE BUFFER ACCESS ROUTINES
;

;
;  GETZ3MSG RETURNS HL POINTING TO DESIRED MESSAGE (OFFSET IN B)
;
Z3MSGA:
	DW	0	;MESSAGES ADDRESS
GETZ3MSG:
	PUSH	D	;SAVE DE
	LHLD	Z3MSGA	;GET ADDRESS OF MESSAGES
	MOV	E,B	;GET OFFSET
	MVI	D,0
	DAD	D	;HL PTS TO MESSAGE OF INTEREST
	POP	D
	RET

MSHLD:
	PUSH	D	;SAVE DE
	XCHG
	CALL	GETZ3MSG	;MAKE HL PT TO MESSAGE
	MOV	M,E	;STORE LOW
	INX	H
	MOV	M,D	;STORE HIGH
	XCHG		;RESTORE HL
	POP	D	;RESTORE DE
	RET

MLHLD:
	PUSH	PSW	;SAVE A
	CALL	GETZ3MSG	;MAKE HL PT TO MESSAGE
	MOV	A,M	;GET LOW
	INX	H
	MOV	H,M	;GET HIGH
	MOV	L,A	;PUT LOW
	POP	PSW	;RESTORE A
	RET

MSTA:
	PUSH	H	;SAVE HL
	CALL	GETZ3MSG	;PT TO MESSAGE WITH HL
	MOV	M,A	;STORE MESSAGE
	POP	H
	RET

MLDA:
	PUSH	H	;SAVE HL
	CALL	GETZ3MSG	;PT TO MESSAGE WITH HL
	MOV	A,M	;GET MESSAGE
	POP	H
	RET

;
;  END OF Z3MSG ACCESS ROUTINES
;

;
;	CONSOLE INPUT INTERCEPT ROUTINE
;
NCONIN:
	MVI	B,07H	;MESSAGE OFFSET
	CALL	GETZ3MSG	;PT TO ZEX MESSAGE BYTE
	MOV	A,M	;GET ZEX MESSAGE
	CPI	2	;SUSPEND INTERCEPT?
	JZ	BCONIN	;GET INPUT VIA BIOS IF USER INPUT ACTIVE
	CPI	1	;PROMPT JUST PRINTED?
	JNZ	NCONNP
	MVI	M,0	;CLEAR ZEX MESSAGE
	LXI	H,STARTM	;PRINT MESSAGE
	CALL	PMSG
;	LDA	PMCHR	;PRINT PROMPT CHAR
;	MOV	C,A
;	CALL	BCONOT
NCONNP:
	MVI	B,08H	;MESSAGE OFFSET
	CALL	MLDA	;GET ZEX RUNNING MESSAGE
	ORA	A	;0=NO
	JZ	WARMX	;ABORT ZEX IF NOT
	LXI	H,0
	DAD	SP	;SAVE RETURN STACK LEVEL
	SHLD	CONSTK
	LXI	SP,MEMTOP	;SET USER STACK
NCONNL:
	CALL	BCONST	;GET CONSOLE STATUS
	ORA	A
	JZ	GETBUF	;GET CHARACTER FROM BUFFER
	CALL	BCONIN	;GET CHARACTER
	CPI	'C'-'@' ;SEE IF TERMINATE CHARACTER
	JZ	ZEXABRT
	CPI	'S'-'@' ;13H
	JNZ	NCONEX
	CALL	BCONIN	;WAIT FOR NEXT CHARACTER
	ANI	7FH
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MLHLD	;PT TO NEXT CHAR
	INX	H
	MOV	M,A
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;RESET PTR TO NEXT CHAR
	MVI	A,'S'-'@'	;13H
NCONEX:
	LHLD	CONSTK	;RESTORE CALLER'S STACK
	SPHL
	RET
;
;  RETURN NEXT CHAR FROM INPUT BUFFER
;
GETBUF:
	LDA	IPSUPFL	;COMBINE PSUPFL AND IPSUPFL TO SET PRINT FLAG
	ORA	A	;0=NO SUPPRESS
	JZ	GBUF0
	MVI	B,01H	;OFFSET TO IF FLAG
	CALL	GETZ3MSG	;PT TO IF FLAG
	MOV	A,M	;GET IF FLAG
	ORA	A	;NO IF?
	JZ	GBUF0
	INX	H
	ANA	M	;SET IF STATE
	JNZ	GBUF0	;CURRENT IF IS TRUE
	MVI	A,0FFH	;SUPPRESS PRINT
	STA	OUTFLG
	JMP	GBUF1
GBUF0:
	LDA	PSUPFL	;SET PRINT SUPPRESS FLAG FOR NCONOT
	STA	OUTFLG
GBUF1:
	CALL	GETCHR	;GET NEXT CHARACTER
	CPI	UICH	;USER INPUT?
	JZ	UISTRT	;YES..SET USER INPUT PENDING FLAG
	CPI	REXEC	;RE-EXECUTE?
	JZ	REXECR	;YES..RESET BUFFER PTR
	CPI	CRWAIT	;CR WAIT?
	JZ	CRWRTN	;YES..WAIT FOR CR
	CPI	CRBWAIT	;CR WAIT WITH RING BELL?
	JZ	CRBWRTN	;YES..WAIT FOR CR AND RING BELL
	CPI	RNG	;RING BELL?
	JZ	RNGBELL	;YES..JUST RING THE BELL
	CPI	MSUP	;MESSAGE SUPPRESS FLAG?
	JZ	MSUPCK	;YES..TOGGLE FLAG
	CPI	PSUP	;PRINT SUPPRESS ?
	JZ	PSUPCK	;YES..TOGGLE FLAG
	CPI	IPS	;FALSE IF PRINT SUPPRESS?
	JZ	IPSUPCK
	CPI	IMON	;IMMEDIATE MODE START ?
	JZ	IMFLGS	;YES..SET FLAG
	CPI	IMOFF	;IMMEDIATE MODE STOP?
	JZ	IMFLGS	;YES..RESET FLAG
	CPI	CR	;CR?
	JNZ	GETEXT	;NO...EXIT
;
; CR, SO RESET PRINT SUPPRESSION BASED ONLY ON IPSUPFL
;
	LDA	IPSUPFL	;COMBINE PSUPFL AND IPSUPFL TO SET PRINT FLAG
	ORA	A	;0=NO SUPPRESS
	JZ	GBUF2
	MVI	B,01H	;MESSAGE OFFSET TO IF FLAG
	CALL	GETZ3MSG	;PT TO IF FLAG
	MOV	A,M	;GET IF FLAG
	ORA	A	;NO IF?
	JZ	GBUF2
	INX	H
	ANA	M	;SET IF STATE
	JNZ	GBUF2	;CURRENT IF IS TRUE
	MVI	A,0FFH	;SUPPRESS PRINT
	STA	OUTFLG
	MVI	A,CR
	JMP	GETEXT
GBUF2:
	XRA	A
	STA	OUTFLG	;YES..RESET PRINT SUPPRESSION
	MVI	A,CR
GETEXT:
	MOV	C,A
	LDA	IMFLG
	CPI	IMON	;IMMEDIATE MODE ?
	MOV	A,C
	JNZ	NCONEX	;NO...RETURN TO CALLER WITH CHAR
	CALL	BCONOT	;YES..IMMEDIATE ECHO TO CONSOLE
	JMP	NCONNL	;...LOOP UNTIL IMOFF
;
;  ^" COMMAND
;
UISTRT:
	MVI	A,2	;SET MESSAGE TO SUSPEND INTERCEPT
	MVI	B,07H	;MESSAGE OFFSET
	CALL	MSTA	;SET MESSAGE
	LHLD	CONSTK	;RESTORE CALLER'S STACK
	SPHL
	JMP	NCONIN	;GET CHAR FROM USER FOR NOW
;
;  ^: COMMAND
;
REXECR:
	MVI	B,0BH	;MESSAGE OFFSET
	CALL	MLHLD	;PT TO FIRST CHAR IN BUFFER
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;SET PTR TO NEXT CHAR
	XRA	A
	STA	IMFLG	;RESET ALL FLAGS
	STA	PSUPFL
	STA	IPSUPFL
	STA	MSUPFL
	JMP	NCONNL	;...LOOP UNTIL ^C
;
;  ^? COMMAND
;
CRWRTN:
	CALL	BCONIN	;GET INPUT CHAR
	CPI	'C'-'@'
	JZ	ZEXABRT	;=^C
	CPI	CR
	JZ	CRWRTX	;=<CR>
	CPI	' '
	JZ	CRWRTX	;=<SP>
	MVI	C,BELL
	CALL	BCONOT	;<>CR
	JMP	CRWRTN
;
;  ^/ COMMAND
;
CRBWRTN:
	LXI	H,DELAY	;SET COUNTER
CRBWR1:
	PUSH	H	;SAVE COUNTER
	CALL	BCONST	;CHECK STATUS
	POP	H	;GET COUNTER
	ORA	A	;SET FLAGS
	JNZ	CRBWR2
	DCX	H	;COUNT DOWN
	MOV	A,H	;DONE?
	ORA	L
	JNZ	CRBWR1
	MVI	C,BELL	;RING BELL
	CALL	BCONOT
	JMP	CRBWRTN
CRBWR2:
	CALL	BCONIN	;GET CHAR
	CPI	'C'-'@'	;ABORT?
	JZ	ZEXABRT
	CPI	CR	;CONT IF <CR>
	JNZ	CRBWRTN
;
;  ^| COMMAND
;
CRWRTX:
	MOV	C,A	;ECHO CR/LF
	CALL	NCONOT
	MVI	C,LF
	CALL	NCONOT
	JMP	GETBUF
;
;  ^* COMMAND
;
RNGBELL:
	MVI	C,BELL	;RING BELL
	CALL	NCONOT
	JMP	GETBUF
;
;  ^. COMMAND
;
PSUPCK:
	LXI	H,PSUPFL
	CMP	M
	JNZ	PSUPST	;SET FLAGS IF NOT EQUAL
	XRA	A	;ELSE RESET FLAGS
PSUPST:
	MOV	M,A	;SET/RESET SAVED FLAG
	JMP	GETBUF	;AND GET NEXT CHARACTER (SETS EXEC FLAG)
;
;  ^& COMMAND
;
IPSUPCK:
	LXI	H,IPSUPFL
	CMP	M
	JNZ	PSUPST	;SET FLAGS IF NOT EQUAL
	XRA	A	;ELSE RESET FLAGS
	JMP	PSUPST	;SET/RESET FLAG IN A
;
;  ^# COMMAND
;
MSUPCK:
	LXI	H,MSUPFL
	CMP	M
	JNZ	MSUPST	;SET FLAGS IF NOT EQUAL
	XRA	A	;ELSE RESET FLAG
MSUPST:
	MOV	M,A	;SET/RESET FLAG
	JMP	GETBUF	;AND GET NEXT CHARACTER
;
;  ^< AND ^> COMMANDS
;
IMFLGS:
	STA	IMFLG	;SET/RESET IMMEDIATE MODE FLAG
	JMP	GETBUF	;GET NEXT CHARACTER
;
;	CONSOLE OUTPUT INTERCEPT ROUTINE
;
NCONOT:
	LDA	OUTFLG	;PRINT SUPPRESSION?
	ORA	A
	RNZ		;YES...IGNORE ECHO
	MOV	A,C
	STA	PMCHR	;SET LAST CHAR OUTPUT
	JMP	BCONOT
;
;  GET NEXT CHAR FROM BUFFER AND TERMINATE ZEX IF END OF BUFFER
;
GETCHR:
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MLHLD	;PT TO NEXT CHAR
	MOV	A,M	;GET IT
	DCX	H	;PT TO FOLLOWING
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;SET PTR TO NEXT CHAR
	CPI	0FFH	;EOB?
	RNZ		;NO...RETURN
;	LHLD	Z3MSG+09H	;PT TO EOB
	INX	H	;POINT TO EOB
	MVI	B,09H	;MESSAGE OFFSET
	CALL	MSHLD	;SET PTR TO NEXT CHAR (LAST CHAR)
	CALL	MOVBAK	;MOVE JUMPS BACK
	CALL	BDOSRST	;RESTORE BDOS ADDRESS
	CALL	PRDONEM
	XRA	A	;TURN OFF ZEX
	MVI	B,08H	;MESSAGE OFFSET
	CALL	MSTA	;TURN OFF ZEX
	LHLD	CONSTK	;GET OLD STACK
	SPHL
	MVI	A,CR	;RETURN CARRIAGE RETURN
	RET

;
;  PRINT DONE MESSAGE WITH FOLLOWING PROMPT CHAR
;
PRDONEM:
	LXI	H,DONEM	;PRINT MESSAGE
	CALL	PMSG
	LDA	PMCHR	;PRINT PROMPT CHAR
	MOV	C,A	;IN C FOR BIOS
	JMP	BCONOT

;
;  RESTORE BDOS JMP IF NECESSARY
;
BDOSRST:
	LHLD	MEMTOP	;SEE IF BDOS+1=MEMTOP
	XCHG
	LHLD	BDOS+1
	MOV	A,E
	SUB	L
	MOV	A,D
	SBB	H
	RNZ		;DON'T REPLACE BDOS JUMP
	INX	D	;PT TO BDOS JUMP
	LDAX	D	;GET LOW ADDRESS
	MOV	L,A	;... IN L
	INX	D
	LDAX	D	;GET HIGH ADDRESS
	MOV	H,A	;... IN H
	SHLD	BDOS+1	;RESET BDOS JUMP
	RET
;
;	^C ABORT EXIT
;
ZEXABRT:
	LXI	SP,MEMTOP	;^C ABORTS ZEX
	LXI	H,ABORTD	;ABORT
	CALL	PMSG
	JMP	WARMX1		;DON'T PRINT DONE MESSAGE
;
;  ABORT ZEX AND RETURN TO ZCPR3
;
WARMX:
	CALL	PRDONEM	;PRINT DONE MESSAGE
;
;  ENTRY POINT TO ABORT ZEX WITHOUT MESSAGE
;
WARMX1:
	XRA	A	;SAY THAT ZEX IS NOT RUNNING
	MVI	B,08H	;MESSAGE OFFSET
	CALL	MSTA	;SET NOT RUNNING
	CALL	MOVBAK	;MOVE JUMPS BACK
	CALL	BDOSRST	;RESTORE BDOS JUMPS
	LHLD	EXMBASE	;MULTIPLE COMMAND LINES ENABLED?
	MOV	A,H	;ANY ON?
	ORA	L
	JZ	WARM	;NONE ON IF ADDRESS IS ZERO, SO JUST WARM BOOT
;
;  THIS SECTION OF CODE CLEARS THE MULTIPLE COMMAND LINE BUFFER
;
	MOV	D,H	;DE PTS TO MULTIPLE COMMAND BUFFER ALSO
	MOV	E,L
	PUSH	H	;SAVE PTR
	LXI	H,4	;PT TO FIRST CHAR OF LINE
	DAD	D
	MVI	M,0	;SET FIRST CHAR OF LINE TO ZERO FOR EOL
	XCHG		;DE PTS TO FIRST CHAR OF LINE
	POP	H	;GET PTR
	MOV	M,E	;STORE ADDRESS OF EMPTY COMMAND (EOL)
	INX	H
	MOV	M,D
	JMP	WARM
;
;	SUBROUTINES
;
MOVBAK:
	LHLD	WARMPT	;MOVE OLD JUMP TABLE BACK TO BIOS
	XCHG
	LXI	H,BSWARM
	MVI	B,12
	CALL	MOVE
	JMP	F121	;CALL 1.2.1 FIX FOR MBASIC	1.1.2
;
MOVE:
	MOV	A,M	;MOVE STRING FROM (HL) TO (DE) FOR LENGTH IN B
	INX	H
	STAX	D
	INX	D
	DCR	B
	JNZ	MOVE
	RET
;
PMSG:
	PUSH	H
	LDA	IPSUPFL	;COMBINE PSUPFL AND IPSUPFL TO SET PRINT FLAG
	ORA	A	;0=NO SUPPRESS
	JZ	PMSG0
	MVI	B,01H	;MESSAGE OFFSET TO IF FLAG
	CALL	GETZ3MSG	;PT TO IF FLAG
	MOV	A,M	;GET IF FLAG
	ORA	A	;NO IF?
	JZ	PMSG0
	INX	H
	ANA	M	;SET IF STATE
	POP	H	;IN CASE OF RETURN
	RZ		;SKIP MESSAGE IF SUPPRESSED
	PUSH	H
PMSG0:
	POP	H
	LDA	MSUPFL	;PRINT MESSAGE AT (HL)
	CPI	MSUP	;MESSAGES SUPPRESSED?
	RZ		;YES..EXIT
PMSGL:
	MOV	A,M	;GET NEXT CHAR
	ORA	A	;END OF MESSAGE?
	RZ		;YES..EXIT
	INX	H	;PT TO NEXT CHAR
	PUSH	H	;SAVE PTR
	MOV	C,A	;OUTPUT CHAR
	CALL	BCONOT
	POP	H	;RESTORE PTR
	JMP	PMSGL
;
;  REPLACE ZEX ROUTINE JUMPS WITH BIOS JUMPS
;
F121:
	LXI	H,BSWARM	; INSURE ONLY BIOS	1.1.2
	LXI	D,NWARM 	;  CALLS FROM NOW ON	1.1.2
	MVI	B,3		;   FOR PROGRAMS	1.1.2
	CALL	MOVE		;    THAT MAY HAVE	1.1.2
	LXI	H,BCONIN	;     COPIED OUR	1.1.2
	LXI	D,NCONIN	;      ADDRESSES AS	1.1.2
	MVI	B,3		;	IF THEY WERE	1.1.2
	CALL	MOVE		;	 IN THE BIOS.	1.1.2
	LXI	H,BCONOT	;  (MBASIC DOES THIS)	1.1.2
	LXI	D,NCONOT	;			1.1.2
	MVI	B,3		;			1.1.2
	JMP	MOVE		;			1.1.2
;
;	WORKING STORAGE AREA
;
ABORTD:
	DB	CR,LF,'[ZEX Aborted]',CR,LF,0
STARTM:
	DB	' ZEX: ',0
DONEM:
	DB	'Done',0

;
	REPT	12	;12 ELT STACK
	DW	0
	ENDM
MEMTOP:
	DW	0
EXMBASE:
	DW	0
CCPJMP:
	DW	0
WARMPT:
	DW	0
;
;	ORIGINAL BIOS JMP TABLE
;
BSWARM:
	JMP	$
BCONST:
	JMP	$
BCONIN:
	JMP	$
BCONOT:
	JMP	$
;
PMCHR:
	DB	0
PSUPFL:
	DB	0
IPSUPFL:
	DB	0
OUTFLG:
	DB	0
NUICH:
	DB	0
IMFLG:
	DB	0
MSUPFL:
	DB	0
CONSTK:
	DW	0
;
?PLEN	SET	$
	IF (?PLEN MOD 8) GT 0
?PLEN	SET	(?PLEN AND 0FFF8H)+8;GET NEXT BOUNDARY
	ENDIF
;
DRVERL	EQU	?PLEN
;
DRVL8	EQU	DRVERL/8	;LENGTH OF RELOCATION BIT MAP
	ORG	DRVERL
;
	ENDIF
;
;	END OF ZEX RELOCATED CODE SEGMENT
;
	END
